package org.dromara.northstar.gateway.tiger;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;
import com.alibaba.fastjson.serializer.SerializerFeature;
import com.tigerbrokers.stock.openapi.client.https.request.TigerHttpRequest;
import com.tigerbrokers.stock.openapi.client.https.response.TigerHttpResponse;
import com.tigerbrokers.stock.openapi.client.struct.enums.MethodName;
import com.tigerbrokers.stock.openapi.client.struct.enums.SecType;
import com.tigerbrokers.stock.openapi.client.util.builder.AccountParamBuilder;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.dromara.northstar.common.constant.ChannelType;
import org.dromara.northstar.common.constant.DateTimeConstant;
import org.dromara.northstar.common.model.core.Contract;
import org.dromara.northstar.common.model.core.Order;
import org.dromara.northstar.common.model.core.Trade;
import org.dromara.northstar.gateway.IContractManager;
import xyz.redtorch.pb.CoreEnum.DirectionEnum;
import xyz.redtorch.pb.CoreEnum.OffsetFlagEnum;
import xyz.redtorch.pb.CoreEnum.OrderStatusEnum;
import xyz.redtorch.pb.CoreEnum.TimeConditionEnum;

import java.time.*;
import java.time.format.DateTimeFormatter;
import java.util.*;

@Slf4j
public class OrderTradeQueryProxy {

    private static final long EXPIRY = 24 * 3600 * 1000;    // 一天以内
    private final TigerHttpClient client;
    private final IContractManager contractMgr;
    private final String accountId;
    private final String gatewayId;

    private Map<Long, Order> orderIds = new HashMap<>();
    private Set<Long> tradeIds = new HashSet<>();

    public OrderTradeQueryProxy(TigerHttpClient client, IContractManager contractMgr, String gatewayId, String accountId) {
        this.client = client;
        this.contractMgr = contractMgr;
        this.accountId = accountId;
        this.gatewayId = gatewayId;
    }

    public List<Order> getDeltaOrder() {
        TigerHttpRequest request = new TigerHttpRequest(MethodName.ORDERS);
        String bizContent = AccountParamBuilder.instance()
                .account(accountId)
                .limit(100)
                .buildJson();

        request.setBizContent(bizContent);
        TigerHttpResponse response = client.execute(request);
        if (!response.isSuccess()) {
            log.warn("查询订单返回异常：{}", response.getMessage());
            throw new IllegalStateException(response.getMessage());
        }
        JSONObject data = JSON.parseObject(response.getData());
        JSONArray items = data.getJSONArray("items");
        List<Order> resultList = new ArrayList<>(100);
        for (int i = 0; i < items.size(); i++) {
            JSONObject json = items.getJSONObject(i);
            long openTime = getOpenTime(json);
            if (expired(openTime)) {
                continue;
            }
            Long id = json.getLong("id");
            Order order = convertOrder(json);
            Order oldOrder = orderIds.get(id);
            if (Objects.isNull(oldOrder) || order.tradedVolume() != oldOrder.tradedVolume() || order.orderStatus() != oldOrder.orderStatus()) {
                orderIds.put(id, order);
                resultList.add(order);
            }
        }
        return resultList;
    }

    private boolean expired(long time) {
        return System.currentTimeMillis() - time > EXPIRY;
    }

    private long getOpenTime(JSONObject json) {
        return json.getLongValue("openTime");
    }

    private Order convertOrder(JSONObject json) {
        Long id = json.getLong("id");
        String orderId = id + "";
        DirectionEnum direction = switch (json.getString("action")) {
            case "BUY" -> DirectionEnum.D_Buy;
            case "SELL" -> DirectionEnum.D_Sell;
            default -> DirectionEnum.D_Unknown;
        };

        OffsetFlagEnum offset = switch (direction) {
            case D_Buy -> OffsetFlagEnum.OF_Open;
            case D_Sell -> OffsetFlagEnum.OF_Close;
            default -> throw new IllegalArgumentException("Unexpected value: " + direction);
        };

        OrderStatusEnum orderStatus = switch (json.getString("status")) {
            case "Filled" -> OrderStatusEnum.OS_AllTraded;
            case "Cancelled" -> OrderStatusEnum.OS_Canceled;
            case "Initial", "PendingSubmit", "Submitted" -> OrderStatusEnum.OS_Touched;
            case "Invalid", "Inactive" -> OrderStatusEnum.OS_Rejected;
            default -> throw new IllegalArgumentException("Unexpected value: " + json.getString("status"));
        };

        TimeConditionEnum timeCondition = switch (json.getString("timeInForce")) {
            case "DAY" -> TimeConditionEnum.TC_GFD;
            case "GTC" -> TimeConditionEnum.TC_GTC;
            case "GTD" -> TimeConditionEnum.TC_GTD;
            default -> throw new IllegalArgumentException("Unexpected value: " + json.getString(""));
        };

        String symbol = json.getString("symbol");
        Contract contract = contractMgr.getContract(ChannelType.TIGER, symbol).contract();
        String orderMsg = json.getString("remark");
        if (orderStatus == OrderStatusEnum.OS_Rejected && !orderIds.containsKey(id)) {
            if (StringUtils.isEmpty(orderMsg)) {
                log.warn("废单反馈：{}", json.toString(SerializerFeature.PrettyFormat));
            } else {
                long openTime = getOpenTime(json);
                LocalDateTime ldt = LocalDateTime.ofInstant(Instant.ofEpochMilli(openTime), ZoneId.systemDefault());
                log.warn("废单信息：{} {} {}", ldt, contract.name(), orderMsg);
            }
        }
        DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyyMMdd");  // Define the pattern
        Instant ins = Instant.ofEpochMilli(getOpenTime(json));
        String tradingDay = LocalDateTime.ofInstant(ins, ZoneOffset.ofHours(0)).toLocalDate().format(DateTimeConstant.D_FORMAT_INT_FORMATTER);
        String orderDate = LocalDateTime.ofInstant(ins, ZoneId.systemDefault()).toLocalDate().format(DateTimeConstant.D_FORMAT_INT_FORMATTER);
        int tradedVol = json.getIntValue("filledQuantity");
        int totalVol = json.getIntValue("totalQuantity");
        double price = (int) (json.getDoubleValue("limitPrice") / contract.priceTick()) * contract.priceTick();

        return Order.builder()
                .gatewayId(gatewayId)
                .contract(contract)
                .orderId(orderId)
                .direction(direction)
                .offsetFlag(offset)
                .orderDate(LocalDate.parse(orderDate, formatter))
                .totalVolume(totalVol)
                .tradedVolume(tradedVol)
                .price(price)
                .tradingDay(LocalDate.parse(tradingDay, formatter))
                .orderTime(LocalTime.parse(LocalDateTime.ofInstant(ins, ZoneId.systemDefault()).toLocalTime().format(DateTimeConstant.T_FORMAT_FORMATTER)))
                .timeCondition(timeCondition)
                .orderStatus(orderStatus).build();

    }

    public List<Trade> getTrades(String symbol) {
        log.trace("查询TIGER成交信息");

        TigerHttpRequest request = new TigerHttpRequest(MethodName.ORDER_TRANSACTIONS);
        String bizContent = AccountParamBuilder.instance()
                .account(accountId)
                .secType(SecType.STK)
                .symbol(symbol)
                .limit(100)
                .buildJson();
        request.setBizContent(bizContent);
        TigerHttpResponse response = client.execute(request);
        if (!response.isSuccess()) {
            log.warn("查询成交返回异常：{}", response.getMessage());
            throw new IllegalStateException(response.getMessage());
        }

        return resolveData(response);
    }

    public List<Trade> getDeltaTrade(Long id) {
        log.trace("查询TIGER成交信息");

        TigerHttpRequest request = new TigerHttpRequest(MethodName.ORDER_TRANSACTIONS);
        String bizContent = AccountParamBuilder.instance()
                .account(accountId)
                .secType(SecType.STK)
                .orderId(id)
                .limit(100)
                .buildJson();
        request.setBizContent(bizContent);
        TigerHttpResponse response = client.execute(request);
        if (!response.isSuccess()) {
            log.warn("查询成交返回异常：{}", response.getMessage());
            throw new IllegalStateException(response.getMessage());
        }

        return resolveData(response);
    }

    private List<Trade> resolveData(TigerHttpResponse response) {
        JSONArray data = JSON.parseObject(response.getData()).getJSONArray("items");
        List<Trade> resultList = new ArrayList<>();
        for (int i = 0; i < data.size(); i++) {
            JSONObject json = data.getJSONObject(i);
            String symbol = json.getString("symbol");
            Contract contract = contractMgr.getContract(ChannelType.TIGER, symbol).contract();
            Long tradeId = json.getLong("id");
            Long tradeTime = json.getLongValue("transactionTime");
            if (tradeIds.contains(tradeId) || expired(tradeTime)) {
                continue;
            }
            tradeIds.add(tradeId);
            String orderId = json.getString("orderId");
            DirectionEnum direction = switch (json.getString("action")) {
                case "BUY" -> DirectionEnum.D_Buy;
                case "SELL" -> DirectionEnum.D_Sell;
                default -> DirectionEnum.D_Unknown;
            };
            OffsetFlagEnum offset = switch (direction) {
                case D_Buy -> OffsetFlagEnum.OF_Open;
                case D_Sell -> OffsetFlagEnum.OF_Close;
                default -> throw new IllegalArgumentException("Unexpected value: " + direction);
            };
            Trade trade = Trade.builder()
                    .gatewayId(gatewayId)
                    .orderId(orderId)
                    .direction(direction)
                    .offsetFlag(offset)
                    .contract(contract)
                    .price(json.getDoubleValue("filledPrice"))
                    .volume(json.getIntValue("filledQuantity"))
                    .tradeDate(LocalDate.parse(json.getString("transactedAt").split(" ")[0]))
                    .tradeTime(LocalTime.parse(json.getString("transactedAt").split(" ")[1]))
                    .tradeTimestamp(tradeTime)
                    .contract(contractMgr.getContract(ChannelType.TIGER, symbol).contract())
                    .build();
            resultList.add(trade);
        }
        return resultList;
    }
}
